// --------------------------------------------------------------------------------------------------------------------
// <copyright file="AgentMessage.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Microsoft.Robotics.Runtime
{
    using System.Diagnostics;
    using System.Runtime.Serialization;
    using System.Threading;

    /// <summary>
    /// Base class for all agent messages.  
    /// </summary>
    [DataContract]
    public class AgentMessage
    {
        /// <summary>
        /// The next available id.
        /// </summary>
        private static long nextId;

        /// <summary>
        /// The timestamp, in ms, when the underlying data for this message was created in hardware.
        /// This is used in synchronizing messages.
        /// </summary>
        [DataMember]
        private readonly long originatingTime;

        /// <summary>
        /// The unique ID of this message.
        /// </summary>
        [DataMember]
        private readonly long id;

        /// <summary>
        /// The name of the agent that created the port the message was sent on.
        /// </summary>
        [DataMember]
        private string creatingAgentName;

        /// <summary>
        /// The agent-specific sequence ID of this message.
        /// </summary>
        [DataMember]
        private long sequenceId;

        /// <summary>
        /// The time when the message was first published. This is used when replaying messages.
        /// </summary>
        [DataMember]
        private long timestamp;

        /// <summary>
        /// Initializes a new instance of the AgentMessage class.
        /// This constructor is intended for Consumer/Producer agents.
        /// </summary>
        /// <param name="initiatingMessage">
        /// The lower-level message that triggered the creation of this message (e.g. a HorizontalDepthProfileMessage is created as a result of a DepthMessage).
        /// The constructor copies the originatingTime from the initiatingMessage to this message, to allow synchronization of messages that have the same real-world origin.
        /// </param>
        public AgentMessage(AgentMessage initiatingMessage)
            : this(initiatingMessage.originatingTime)
        {
        }

        /// <summary>
        /// Initializes a new instance of the AgentMessage class.
        /// This constructor is intended for Producer-only agents.
        /// </summary>
        /// <param name="originatingTime">
        /// The timestamp, in ms, when the underlying data for this message was created in hardware.
        /// This is used in synchronizing messages.
        /// Note that the origin of time is unknown to the framework - it is a function of the machine creating the message. 
        /// When the framework runs on a single machine, all timestamps have the same reference, 
        /// but when they run on different machines this is not true anymore because the clocks are not synchronized.  
        /// </param>
        protected internal AgentMessage(long originatingTime)
        {
            this.originatingTime = originatingTime;
            this.id = Interlocked.Increment(ref nextId);
        }

        /// <summary>
        /// Gets the timestamp in milliseconds (as opposed to hardware-dependent ticks) when the message was published.
        /// </summary>
        public long Timestamp
        {
            get
            {
                return this.timestamp;
            }

            internal set
            {
                this.timestamp = value;
            }
        }

        /// <summary>
        /// Gets the name of the agent that created the port the message was sent on.
        /// </summary>
        public string CreatingAgentName
        {
            get 
            { 
                return this.creatingAgentName; 
            }

            internal set
            {
                this.creatingAgentName = value;
            }
        }

        /// <summary>
        /// Gets the timestamp when the message (or its underlying data) was created.
        /// This is used in synchronizing messages.
        /// </summary>
        public long OriginatingTime
        {
            get { return this.originatingTime; }
        }

        /// <summary>
        /// Gets the unique ID of this message.
        /// </summary>
        public long Id
        {
            get
            {
                return this.id;
            }
        }

        /// <summary>
        /// Gets or sets the agent-specific sequence ID of this message.
        /// </summary>
        public long SequenceId
        {
            get
            {
                return this.sequenceId;
            }

            set
            {
                this.sequenceId = value;
            }
        }

        /// <summary>
        /// Returns the current time in ms, based on the high-resolution timer.
        /// </summary>
        /// <returns>The current time based on the current number of ticks in the timer mechanism.</returns>
        public static long GetCurrentTime()
        {
            return Stopwatch.GetTimestamp() * 1000 / Stopwatch.Frequency;
        }
    }
}
